/******************************************************************
 *
 * Copyright 2019 Samsung Electronics All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ******************************************************************/

/****************************************************************************
 * arch/xtensa/src/esp32/esp32_serial.c
 *
 *   Copyright (C) 2016-2017 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <gnutt@nuttx.org>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <tinyara/config.h>

#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>
#include <semaphore.h>
#include <string.h>
#include <errno.h>
#include <debug.h>

#ifdef CONFIG_SERIAL_TERMIOS
#include <termios.h>
#endif

#include <tinyara/irq.h>
#include <tinyara/arch.h>
#include <tinyara/serial/serial.h>

#include <arch/serial.h>
#include <arch/board/board.h>

#include "xtensa.h"
#include "chip/esp32_soc.h"
#include "chip/esp32_iomux.h"
#include "chip/esp32_gpio_sigmap.h"
#include "chip/esp32_uart.h"
#include "rom/esp32_gpio.h"
#include "esp32_config.h"
#include "esp32_gpio.h"
#include "esp32_cpuint.h"

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

#ifdef USE_SERIALDRIVER

/* Which UART with be tty0/console and which tty1-2? */

/* First pick the console and ttys0.  This could be any of UART0-5 */

#if defined(CONFIG_UART0_SERIAL_CONSOLE)
#define CONSOLE_DEV         g_uart0port	/* UART0 is console */
#define TTYS0_DEV           g_uart0port	/* UART0 is ttyS0 */
#define UART0_ASSIGNED      1
#elif defined(CONFIG_UART1_SERIAL_CONSOLE)
#define CONSOLE_DEV         g_uart1port	/* UART1 is console */
#define TTYS0_DEV           g_uart1port	/* UART1 is ttyS0 */
#define UART1_ASSIGNED      1
#elif defined(CONFIG_UART2_SERIAL_CONSOLE)
#define CONSOLE_DEV         g_uart2port	/* UART2 is console */
#define TTYS0_DEV           g_uart2port	/* UART2 is ttyS0 */
#define UART2_ASSIGNED      1
#else
#undef CONSOLE_DEV				/* No console */
#if defined(CONFIG_ESP32_UART0)
#define TTYS0_DEV           g_uart0port	/* UART0 is ttyS0 */
#define UART0_ASSIGNED      1
#elif defined(CONFIG_ESP32_UART1)
#define TTYS0_DEV           g_uart1port	/* UART1 is ttyS0 */
#define UART1_ASSIGNED      1
#elif defined(CONFIG_ESP32_UART2)
#define TTYS0_DEV           g_uart2port	/* UART2 is ttyS0 */
#define UART2_ASSIGNED      1
#endif
#endif

/* Pick ttys1.  This could be any of UART0-2 excluding the console
 * UART.
 */

#if defined(CONFIG_ESP32_UART0) && !defined(UART0_ASSIGNED)
#define TTYS1_DEV           g_uart0port	/* UART0 is ttyS1 */
#define UART0_ASSIGNED      1
#elif defined(CONFIG_ESP32_UART1) && !defined(UART1_ASSIGNED)
#define TTYS1_DEV           g_uart1port	/* UART1 is ttyS1 */
#define UART1_ASSIGNED      1
#elif defined(CONFIG_ESP32_UART2) && !defined(UART2_ASSIGNED)
#define TTYS1_DEV           g_uart2port	/* UART2 is ttyS1 */
#define UART2_ASSIGNED      1
#endif

/* Pick ttys2.  This could be one of UART1-2. It can't be UART0
 * because that was either assigned as ttyS0 or ttys1.  One of these
 * could also be the console.
 */

#if defined(CONFIG_ESP32_UART1) && !defined(UART1_ASSIGNED)
#define TTYS2_DEV           g_uart1port	/* UART1 is ttyS2 */
#define UART1_ASSIGNED      1
#elif defined(CONFIG_ESP32_UART2) && !defined(UART2_ASSIGNED)
#define TTYS2_DEV           g_uart2port	/* UART2 is ttyS2 */
#define UART2_ASSIGNED      1
#endif

/* UART source clock for BAUD generation */

#define UART_CLK_FREQ         APB_CLK_FREQ

/****************************************************************************
 * Private Types
 ****************************************************************************/
/* Constant properties of the UART.  Other configuration setting may be
 * changable via Termios IOCTL calls.
 */

struct esp32_config_s {
	const uint32_t uartbase;	/* Base address of UART registers */
	uint8_t periph;				/* UART peripheral ID */
	uint8_t irq;				/* IRQ number assigned to the peripheral */
	uint8_t txpin;				/* Tx pin number (0-39) */
	uint8_t rxpin;				/* Rx pin number (0-39) */
	uint8_t txsig;				/* Tx signal */
	uint8_t rxsig;				/* Rx signal */
#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL)
	uint8_t rtspin;				/* RTS pin number (0-39) */
	uint8_t ctspin;				/* CTS pin number (0-39) */
	uint8_t rtssig;				/* RTS signal */
	uint8_t ctssig;				/* CTS signal */
#endif
};

/* Current state of the UART */

struct esp32_dev_s {
	const struct esp32_config_s *config;	/* Constant configuration */
	uint32_t baud;				/* Configured baud */
	uint32_t status;			/* Saved status bits */
	uint8_t cpuint;				/* CPU interrupt assigned to this UART */
	uint8_t parity;				/* 0=none, 1=odd, 2=even */
	uint8_t bits;				/* Number of bits (5-9) */
	bool stopbits2;				/* true: Configure with 2 stop bits instead of 1 */
#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL)
	bool flowc;					/* Input flow control (RTS) enabled */
#endif
};

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

static int esp32_setup(struct uart_dev_s *dev);
static void esp32_shutdown(struct uart_dev_s *dev);
static int esp32_attach(struct uart_dev_s *dev);
static void esp32_detach(struct uart_dev_s *dev);
static int esp32_interrupt(int cpuint, void *context, FAR void *arg);
static int esp32_ioctl(FAR struct uart_dev_s *dev, int cmd, unsigned long arg);
static int esp32_receive(struct uart_dev_s *dev, unsigned int *status);
static void esp32_rxint(struct uart_dev_s *dev, bool enable);
static bool esp32_rxavailable(struct uart_dev_s *dev);
static void esp32_send(struct uart_dev_s *dev, int ch);
static void esp32_txint(struct uart_dev_s *dev, bool enable);
static bool esp32_txready(struct uart_dev_s *dev);
static bool esp32_txempty(struct uart_dev_s *dev);

/****************************************************************************
 * Private Data
 ****************************************************************************/

static const struct uart_ops_s g_uart_ops = {
	.setup = esp32_setup,
	.shutdown = esp32_shutdown,
	.attach = esp32_attach,
	.detach = esp32_detach,
	.ioctl = esp32_ioctl,
	.receive = esp32_receive,
	.rxint = esp32_rxint,
	.rxavailable = esp32_rxavailable,
#ifdef CONFIG_SERIAL_IFLOWCONTROL
	.rxflowcontrol = NULL,
#endif
	.send = esp32_send,
	.txint = esp32_txint,
	.txready = esp32_txready,
	.txempty = esp32_txempty,
};

/* I/O buffers */

#ifdef CONFIG_ESP32_UART0
static char g_uart0rxbuffer[CONFIG_UART0_RXBUFSIZE];
static char g_uart0txbuffer[CONFIG_UART0_TXBUFSIZE];
#endif
#ifdef CONFIG_ESP32_UART1
static char g_uart1rxbuffer[CONFIG_UART1_RXBUFSIZE];
static char g_uart1txbuffer[CONFIG_UART1_TXBUFSIZE];
#endif
#ifdef CONFIG_ESP32_UART2
static char g_uart2rxbuffer[CONFIG_UART2_RXBUFSIZE];
static char g_uart2txbuffer[CONFIG_UART2_TXBUFSIZE];
#endif

/* This describes the state of the UART0 port. */

#ifdef CONFIG_ESP32_UART0
static const struct esp32_config_s g_uart0config = {
	.uartbase = DR_REG_UART_BASE,
	.periph = ESP32_PERIPH_UART,
	.irq = ESP32_IRQ_UART,
	.txpin = CONFIG_ESP32_UART0_TXPIN,
	.rxpin = CONFIG_ESP32_UART0_RXPIN,
	.txsig = U0TXD_OUT_IDX,
	.rxsig = U0RXD_IN_IDX,
#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL)
	.rtspin = CONFIG_ESP32_UART0_RTSPIN,
	.ctspin = CONFIG_ESP32_UART0_CTSPIN,
	.rtssig = U0RTS_OUT_IDX,
	.ctssig = U0CTS_IN_IDX,
#endif
};

static struct esp32_dev_s g_uart0priv = {
	.config = &g_uart0config,
	.baud = CONFIG_UART0_BAUD,
	.parity = CONFIG_UART0_PARITY,
	.bits = CONFIG_UART0_BITS,
	.stopbits2 = CONFIG_UART0_2STOP,
};

static uart_dev_t g_uart0port = {
	.recv = {
		.size = CONFIG_UART0_RXBUFSIZE,
		.buffer = g_uart0rxbuffer,
	},
	.xmit = {
		.size = CONFIG_UART0_TXBUFSIZE,
		.buffer = g_uart0txbuffer,
	},
	.ops = &g_uart_ops,
	.priv = &g_uart0priv,
};
#endif

/* This describes the state of the UART1 port. */

#ifdef CONFIG_ESP32_UART1
static const struct esp32_config_s g_uart1config = {
	.uartbase = DR_REG_UART1_BASE,
	.periph = ESP32_PERIPH_UART1,
	.irq = ESP32_IRQ_UART1,
	.txpin = CONFIG_ESP32_UART1_TXPIN,
	.rxpin = CONFIG_ESP32_UART1_RXPIN,
	.txsig = U1TXD_OUT_IDX,
	.rxsig = U1RXD_IN_IDX,
#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL)
	.rtspin = CONFIG_ESP32_UART1_RTSPIN,
	.ctspin = CONFIG_ESP32_UART1_CTSPIN,
	.rtssig = U1RTS_OUT_IDX,
	.ctssig = U1CTS_IN_IDX,
#endif
};

static struct esp32_dev_s g_uart1priv = {
	.config = &g_uart1config,
	.baud = CONFIG_UART1_BAUD,
	.parity = CONFIG_UART1_PARITY,
	.bits = CONFIG_UART1_BITS,
	.stopbits2 = CONFIG_UART1_2STOP,
};

static uart_dev_t g_uart1port = {
	.recv = {
		.size = CONFIG_UART1_RXBUFSIZE,
		.buffer = g_uart1rxbuffer,
	},
	.xmit = {
		.size = CONFIG_UART1_TXBUFSIZE,
		.buffer = g_uart1txbuffer,
	},
	.ops = &g_uart_ops,
	.priv = &g_uart1priv,
};
#endif

/* This describes the state of the UART2 port. */

#ifdef CONFIG_ESP32_UART2
static const struct esp32_config_s g_uart2config = {
	.uartbase = DR_REG_UART2_BASE,
	.periph = ESP32_PERIPH_UART2,
	.irq = ESP32_IRQ_UART2,
	.txpin = CONFIG_ESP32_UART2_TXPIN,
	.rxpin = CONFIG_ESP32_UART2_RXPIN,
	.txsig = U2TXD_OUT_IDX,
	.rxsig = U2RXD_IN_IDX,
#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL)
	.rtspin = CONFIG_ESP32_UART2_RTSPIN,
	.ctspin = CONFIG_ESP32_UART2_CTSPIN,
	.rtssig = U2RTS_OUT_IDX,
	.ctssig = U2CTS_IN_IDX,
#endif
};

static struct esp32_dev_s g_uart2priv = {
	.config = &g_uart2config,
	.baud = CONFIG_UART2_BAUD,
	.parity = CONFIG_UART2_PARITY,
	.bits = CONFIG_UART2_BITS,
	.stopbits2 = CONFIG_UART2_2STOP,
};

static uart_dev_t g_uart2port = {
	.recv = {
		.size = CONFIG_UART2_RXBUFSIZE,
		.buffer = g_uart2rxbuffer,
	},
	.xmit = {
		.size = CONFIG_UART2_TXBUFSIZE,
		.buffer = g_uart2txbuffer,
	},
	.ops = &g_uart_ops,
	.priv = &g_uart2priv,
};
#endif

void uart_tx_wait_idle(uint8_t uart_no)
{
	int status = getreg32(UART_STATUS_REG(uart_no));
	while ((status >> 24) & UART_ST_UTX_OUT) {
		;
	}
}

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Name: esp32_serialin
 ****************************************************************************/

static inline uint32_t esp32_serialin(struct esp32_dev_s *priv, int offset)
{
	return getreg32(priv->config->uartbase + offset);
}

/****************************************************************************
 * Name: esp32_serialout
 ****************************************************************************/

static inline void esp32_serialout(struct esp32_dev_s *priv, int offset, uint32_t value)
{
	putreg32(value, priv->config->uartbase + offset);
}

/****************************************************************************
 * Name: esp32_restoreuartint
 ****************************************************************************/

static inline void esp32_restoreuartint(struct esp32_dev_s *priv, uint32_t intena)
{
	/* Restore the previous interrupt state (assuming all interrupts disabled) */
	esp32_serialout(priv, UART_INT_ENA_OFFSET, intena);
}

/****************************************************************************
 * Name: esp32_disableallints
 ****************************************************************************/

static void esp32_disableallints(struct esp32_dev_s *priv, uint32_t *intena)
{
	irqstate_t flags;

	/* The following must be atomic */

	flags = up_irq_save();

	if (intena) {
		/* Return the current interrupt mask */

		*intena = esp32_serialin(priv, UART_INT_ENA_OFFSET);
	}

	/* Disable all interrupts */

	esp32_serialout(priv, UART_INT_ENA_OFFSET, 0);
	up_irq_restore(flags);
}

/****************************************************************************
 * Name: esp32_setup
 *
 * Description:
 *   Configure the UART baud, bits, parity, etc. This method is called the
 *   first time that the serial port is opened.
 *
 ****************************************************************************/

static int esp32_setup(struct uart_dev_s *dev)
{
#ifndef CONFIG_SUPPRESS_UART_CONFIG
	struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv;
	uint32_t clkdiv;
	uint32_t regval;
	uint32_t conf0;

	/* Note: The logic here depends on the fact that that the UART module
	 * was enabled and the pins were configured in esp32_lowsetup().
	 */

	/* The shutdown method will put the UART in a known, disabled state */

	esp32_shutdown(dev);

	/* Set up the CONF0 register. */

	conf0 = UART_TICK_REF_ALWAYS_ON;

#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL)
	/* Check if flow control is enabled */

	if (priv->flowc) {
		/* Enable hardware flow control */

		conf0 |= UART_TX_FLOW_EN;
	}
#endif

	/* OR in settings for the selected number of bits */

	if (priv->bits == 5) {
		/* 0=5 bits */
	} else if (priv->bits == 6) {
		conf0 |= (1 << UART_BIT_NUM_S);	/* 1=6 bits */
	} else if (priv->bits == 7) {
		conf0 |= (2 << UART_BIT_NUM_S);	/* 2=7 bits */
	} else {					/* if (priv->bits == 8) */
		conf0 |= (3 << UART_BIT_NUM_S);	/* 3=8 bits */
	}

	/* OR in settings for the selected parity */

	if (priv->parity == 1) {
		conf0 |= UART_PARITY_EN;
	} else if (priv->parity == 2) {
		conf0 |= UART_PARITY_EN | UART_PARITY;
	}

	/* OR in settings for the number of stop bits */

	if (priv->stopbits2) {
		conf0 |= 2 << UART_STOP_BIT_NUM_S;
	} else {
		conf0 |= 1 << UART_STOP_BIT_NUM_S;
	}

	/* Configure the UART BAUD */

	clkdiv = (UART_CLK_FREQ << 4) / priv->baud;

	regval = (clkdiv >> 4) << UART_CLKDIV_S;
	regval |= (clkdiv & 15) << UART_CLKDIV_FRAG_S;
	esp32_serialout(priv, UART_CLKDIV_OFFSET, regval);

	/* Configure UART pins
	 *
	 * Internal signals can be output to multiple GPIO pads.
	 * But only one GPIO pad can connect with input signal
	 */

	esp32_configgpio(priv->config->txpin, OUTPUT_FUNCTION_2);
	gpio_matrix_out(priv->config->txpin, priv->config->txsig, 0, 0);

	esp32_configgpio(priv->config->rxpin, INPUT_FUNCTION_2);
	gpio_matrix_in(priv->config->rxpin, priv->config->rxsig, 0);

#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL)
	esp32_configgpio(priv->config->rtspin, OUTPUT_FUNCTION_2);
	gpio_matrix_out(priv->config->rtspin, priv->config->rtssig, 0, 0);

	esp32_configgpio(priv->config->ctspin, INPUT_FUNCTION_2);
	gpio_matrix_in(priv->config->ctspin, priv->config->ctssig, 0);
#endif

	/* Enable RX and error interrupts.  Clear and pending interrtupt */

	esp32_serialout(priv, UART_INT_CLR_OFFSET, 0xffffffff);

	/* Configure and enable the UART */

	esp32_serialout(priv, UART_CONF0_OFFSET, conf0);
	regval = (112 << UART_RXFIFO_FULL_THRHD_S) | (0x02 << UART_RX_TOUT_THRHD_S) | UART_RX_TOUT_EN;
	esp32_serialout(priv, UART_CONF1_OFFSET, regval);
#endif

	return OK;
}

/****************************************************************************
 * Name: esp32_shutdown
 *
 * Description:
 *   Disable the UART.  This method is called when the serial port is
 *   closed.  It is assumed that esp32_detach was called earlier in the
 *   shutdown sequence.
 *
 ****************************************************************************/

static void esp32_shutdown(struct uart_dev_s *dev)
{
	struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv;
	uint32_t status;

	/* Wait for outgoing FIFO to clear.   The ROM bootloader does not flush
	 * the FIFO before handing over to user code, so some of this output is
	 * not currently seen when the UART is reconfigured in early stages of
	 * startup.
	 */

	do {
		status = esp32_serialin(priv, UART_STATUS_OFFSET);
	} while (((status & UART_TXFIFO_CNT_M) >> UART_TXFIFO_CNT_S) != 0);

	/* Disable all UART interrupts */

	esp32_disableallints(priv, NULL);

	/* Revert pins to inputs and detach UART signals */

	esp32_configgpio(priv->config->txpin, INPUT);
	gpio_matrix_out(priv->config->txpin, MATRIX_DETACH_OUT_SIG, true, false);

	esp32_configgpio(priv->config->rxpin, INPUT);
	gpio_matrix_in(priv->config->rxpin, MATRIX_DETACH_IN_LOW_PIN, false);

#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL)
	esp32_configgpio(priv->config->rtspin, INPUT);
	gpio_matrix_out(priv->config->rtspin, MATRIX_DETACH_OUT_SIG, true, false);

	esp32_configgpio(priv->config->ctspin, INPUT);
	gpio_matrix_in(priv->config->ctspin, MATRIX_DETACH_IN_LOW_PIN, false);
#endif

	/* Unconfigure and disable the UART */

	esp32_serialout(priv, UART_CONF0_OFFSET, 0);
	esp32_serialout(priv, UART_CONF1_OFFSET, 0);

	esp32_serialout(priv, UART_INT_ENA_OFFSET, 0);
	esp32_serialout(priv, UART_INT_CLR_OFFSET, 0xffffffff);
}

/****************************************************************************
 * Name: esp32_attach
 *
 * Description:
 *   Configure the UART to operation in interrupt driven mode.  This method is
 *   called when the serial port is opened.  Normally, this is just after the
 *   the setup() method is called, however, the serial console may operate in
 *   a non-interrupt driven mode during the boot phase.
 *
 *   RX and TX interrupts are not enabled when by the attach method (unless the
 *   hardware supports multiple levels of interrupt enabling).  The RX and TX
 *   interrupts are not enabled until the txint() and rxint() methods are called.
 *
 ****************************************************************************/

static int esp32_attach(struct uart_dev_s *dev)
{
	struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv;
	int cpu;
	int ret = OK;

	/* Allocate a level-sensitive, priority 1 CPU interrupt for the UART */

	priv->cpuint = esp32_alloc_levelint(1);
	if (priv->cpuint < 0) {
		/* Failed to allocate a CPU interrupt of this type */

		return priv->cpuint;
	}

	/* Set up to receive peripheral interrupts on the current CPU */

#ifdef CONFIG_SMP
	cpu = up_cpu_index();
#else
	cpu = 0;
#endif

	/* Attach the GPIO peripheral to the allocated CPU interrupt */

	up_disable_irq(priv->cpuint);
	esp32_attach_peripheral(cpu, priv->config->periph, priv->cpuint);

	/* Attach and enable the IRQ */

	ret = irq_attach(priv->config->irq, esp32_interrupt, dev);
	if (ret == OK) {
		/* Enable the CPU interrupt (RX and TX interrupts are still disabled
		 * in the UART
		 */

		up_enable_irq(priv->cpuint);
	}

	return ret;
}

/****************************************************************************
 * Name: esp32_detach
 *
 * Description:
 *   Detach UART interrupts.  This method is called when the serial port is
 *   closed normally just before the shutdown method is called.  The exception
 *   is the serial console which is never shutdown.
 *
 ****************************************************************************/

static void esp32_detach(struct uart_dev_s *dev)
{
	struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv;
	int cpu;

	/* Disable and detach the CPU interrupt */

	up_disable_irq(priv->cpuint);
	irq_detach(priv->config->irq);

	/* Disassociate the peripheral interrupt from the CPU interrupt */

#ifdef CONFIG_SMP
	cpu = up_cpu_index();
#else
	cpu = 0;
#endif

	esp32_detach_peripheral(cpu, priv->config->periph, priv->cpuint);

	/* And release the CPU interrupt */

	esp32_free_cpuint(priv->cpuint);
	priv->cpuint = 0xff;
}

/****************************************************************************
 * Name: esp32_interrupt
 *
 * Description:
 *   This is the common UART interrupt handler.  It will be invoked
 *   when an interrupt received on the device.  It should call
 *   uart_transmitchars or uart_receivechar to perform the appropriate data
 *   transfers.
 *
 ****************************************************************************/

static int esp32_interrupt(int cpuint, void *context, FAR void *arg)
{
	struct uart_dev_s *dev = (struct uart_dev_s *)arg;
	struct esp32_dev_s *priv;
	uint32_t regval;
	uint32_t status;
	uint32_t enabled;
	unsigned int nfifo;
	int passes;
	bool handled;

	DEBUGASSERT(dev != NULL && dev->priv != NULL);
	priv = (struct esp32_dev_s *)dev->priv;

	/* Loop until there are no characters to be transferred or, until we have
	 * been looping for a long time.
	 */

	handled = true;
	for (passes = 0; passes < 256 && handled; passes++) {
		handled = false;
		priv->status = esp32_serialin(priv, UART_INT_RAW_OFFSET);
		status = esp32_serialin(priv, UART_STATUS_OFFSET);
		enabled = esp32_serialin(priv, UART_INT_ENA_OFFSET);

		/* Clear pending interrupts */

		regval = (UART_RXFIFO_FULL_INT_CLR | UART_FRM_ERR_INT_CLR | UART_RXFIFO_TOUT_INT_CLR | UART_TX_DONE_INT_CLR | UART_TXFIFO_EMPTY_INT_CLR);
		esp32_serialout(priv, UART_INT_CLR_OFFSET, regval);

		/* Are Rx interrupts enabled?  The upper layer may hold off Rx input
		 * by disabling the Rx interrupts if there is no place to saved the
		 * data, possibly resulting in an overrun error.
		 */

		if ((enabled & (UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA)) != 0) {
			/* Is there any data waiting in the Rx FIFO? */

			nfifo = (status & UART_RXFIFO_CNT_M) >> UART_RXFIFO_CNT_S;
			if (nfifo > 0) {
				/* Received data in the RXFIFO! ... Process incoming bytes */

				uart_recvchars(dev);
				handled = true;
			}
		}

		/* Are Tx interrupts enabled?  The upper layer will disable Tx interrupts
		 * when it has nothing to send.
		 */

		if ((enabled & (UART_TX_DONE_INT_ENA | UART_TXFIFO_EMPTY_INT_ENA)) != 0) {
			nfifo = (status & UART_TXFIFO_CNT_M) >> UART_TXFIFO_CNT_S;
			if (nfifo < 0x7f) {
				/* The TXFIFO is not full ... process outgoing bytes */

				uart_xmitchars(dev);
				handled = true;
			}
		}
	}

	return OK;
}

/****************************************************************************
 * Name: esp32_ioctl
 *
 * Description:
 *   All ioctl calls will be routed through this method
 *
 ****************************************************************************/

static int esp32_ioctl(FAR struct uart_dev_s *dev, int cmd, unsigned long arg)
{
	int ret = OK;

	switch (cmd) {
#ifdef CONFIG_SERIAL_TIOCSERGSTRUCT
	case TIOCSERGSTRUCT: {
		struct esp32_dev_s *user = (struct esp32_dev_s *)arg;
		if (!user) {
			ret = -EINVAL;
		} else {
			memcpy(user, dev, sizeof(struct esp32_dev_s));
		}
	}
	break;
#endif

#ifdef CONFIG_SERIAL_TERMIOS
	case TCGETS: {
		struct termios *termiosp = (struct termios *)arg;
		struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv;

		if (!termiosp) {
			ret = -EINVAL;
			break;
		}

		/* Return baud */

		cfsetispeed(termiosp, priv->baud);

		/* Return parity */

		termiosp->c_cflag = ((priv->parity != 0) ? PARENB : 0) | ((priv->parity == 1) ? PARODD : 0);

		/* Return stop bits */

		termiosp->c_cflag |= (priv->stopbits2) ? CSTOPB : 0;

		/* Return flow control */

#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL)
		termiosp->c_cflag |= (priv->flowc) ? (CCTS_OFLOW | CRTS_IFLOW) : 0;
#endif
		/* Return number of bits */

		switch (priv->bits) {
		case 5:
			termiosp->c_cflag |= CS5;
			break;

		case 6:
			termiosp->c_cflag |= CS6;
			break;

		case 7:
			termiosp->c_cflag |= CS7;
			break;

		default:
		case 8:
			termiosp->c_cflag |= CS8;
			break;

		case 9:
			termiosp->c_cflag |= CS8 /* CS9 */ ;
			break;
		}
	}
	break;

	case TCSETS: {
		struct termios *termiosp = (struct termios *)arg;
		struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv;
		uint32_t baud;
		uint32_t intena;
		uint8_t parity;
		uint8_t nbits;
		bool stop2;
#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL)
		bool flowc;
#endif

		if (!termiosp) {
			ret = -EINVAL;
			break;
		}

		/* Decode baud. */

		ret = OK;
		baud = cfgetispeed(termiosp);

		/* Decode number of bits */

		switch (termiosp->c_cflag & CSIZE) {
		case CS5:
			nbits = 5;
			break;

		case CS6:
			nbits = 6;
			break;

		case CS7:
			nbits = 7;
			break;

		case CS8:
			nbits = 8;
			break;
		default:
			ret = -EINVAL;
			break;
		}

		/* Decode parity */

		if ((termiosp->c_cflag & PARENB) != 0) {
			parity = (termiosp->c_cflag & PARODD) ? 1 : 2;
		} else {
			parity = 0;
		}

		/* Decode stop bits */

		stop2 = (termiosp->c_cflag & CSTOPB) != 0;

		/* Decode flow control */

#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL)
		flowc = (termiosp->c_cflag & (CCTS_OFLOW | CRTS_IFLOW)) != 0;
#endif
		/* Verify that all settings are valid before committing */

		if (ret == OK) {
			/* Commit */

			priv->baud = baud;
			priv->parity = parity;
			priv->bits = nbits;
			priv->stopbits2 = stop2;
#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL)
			priv->flowc = flowc;
#endif
			/* effect the changes immediately - note that we do not
			 * implement TCSADRAIN / TCSAFLUSH
			 */

			esp32_disableallints(priv, &intena);
			ret = esp32_setup(dev);

			/* Restore the interrupt state */

			esp32_restoreuartint(priv, intena);
		}
	}
	break;
#endif							/* CONFIG_SERIAL_TERMIOS */

	default:
		ret = -ENOTTY;
		break;
	}

	return ret;
}

/****************************************************************************
 * Name: esp32_receive
 *
 * Description:
 *   Called (usually) from the interrupt level to receive one
 *   character from the UART.  Error bits associated with the
 *   receipt are provided in the return 'status'.
 *
 ****************************************************************************/

static int esp32_receive(struct uart_dev_s *dev, unsigned int *status)
{
	struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv;

	/* Return the error information in the saved status */

	*status = (unsigned int)priv->status;
	priv->status = 0;

	/* Then return the actual received byte */

	return (int)(esp32_serialin(priv, UART_FIFO_OFFSET) & UART_RXFIFO_RD_BYTE_M);
}

/****************************************************************************
 * Name: esp32_rxint
 *
 * Description:
 *   Call to enable or disable RXRDY interrupts
 *
 ****************************************************************************/

static void esp32_rxint(struct uart_dev_s *dev, bool enable)
{
	struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv;
	irqstate_t flags;
	int regval;

	flags = up_irq_save();

	if (enable) {
		/* Receive an interrupt when their is anything in the Rx data register (or an Rx
		 * timeout occurs).
		 */

#ifndef CONFIG_SUPPRESS_SERIAL_INTS
		regval = esp32_serialin(priv, UART_INT_ENA_OFFSET);
		regval |= (UART_RXFIFO_FULL_INT_ENA | UART_FRM_ERR_INT_ENA | UART_RXFIFO_TOUT_INT_ENA);
		esp32_serialout(priv, UART_INT_ENA_OFFSET, regval);
#endif
	} else {
		/* Disable the RX interrupts */

		regval = esp32_serialin(priv, UART_INT_ENA_OFFSET);
		regval &= ~(UART_RXFIFO_FULL_INT_ENA | UART_FRM_ERR_INT_ENA | UART_RXFIFO_TOUT_INT_ENA);
		esp32_serialout(priv, UART_INT_ENA_OFFSET, regval);
	}

	up_irq_restore(flags);
}

/****************************************************************************
 * Name: esp32_rxavailable
 *
 * Description:
 *   Return true if the receive holding register is not empty
 *
 ****************************************************************************/

static bool esp32_rxavailable(struct uart_dev_s *dev)
{
	struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv;
	return ((esp32_serialin(priv, UART_STATUS_OFFSET) & UART_RXFIFO_CNT_M) > 0);
}

/****************************************************************************
 * Name: esp32_send
 *
 * Description:
 *   This method will send one byte on the UART.
 *
 ****************************************************************************/

static void esp32_send(struct uart_dev_s *dev, int ch)
{
	struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv;
	esp32_serialout(priv, UART_FIFO_OFFSET, (uint32_t) ch);
}

/****************************************************************************
 * Name: esp32_txint
 *
 * Description:
 *   Call to enable or disable TX interrupts
 *
 ****************************************************************************/

static void esp32_txint(struct uart_dev_s *dev, bool enable)
{
	struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv;
	irqstate_t flags;
	int regval;

	flags = up_irq_save();

	if (enable) {
		/* Set to receive an interrupt when the TX holding register register
		 * is empty
		 */

#ifndef CONFIG_SUPPRESS_SERIAL_INTS
		regval = esp32_serialin(priv, UART_INT_ENA_OFFSET);
		regval |= (UART_TX_DONE_INT_ENA | UART_TXFIFO_EMPTY_INT_ENA);
		esp32_serialout(priv, UART_INT_ENA_OFFSET, regval);

		/* Fake a TX interrupt here by just calling uart_xmitchars() with
		 * interrupts disabled (note this may recurse).
		 */

		uart_xmitchars(dev);
#endif
	} else {
		/* Disable the TX interrupt */

		regval = esp32_serialin(priv, UART_INT_ENA_OFFSET);
		regval &= ~(UART_TX_DONE_INT_ENA | UART_TXFIFO_EMPTY_INT_ENA);
		esp32_serialout(priv, UART_INT_ENA_OFFSET, regval);
	}

	up_irq_restore(flags);
}

/****************************************************************************
 * Name: esp32_txready
 *
 * Description:
 *   Return true if the transmit holding register is empty (TXRDY)
 *
 ****************************************************************************/

static bool esp32_txready(struct uart_dev_s *dev)
{
	struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv;
	return (((esp32_serialin(priv, UART_STATUS_OFFSET) & UART_TXFIFO_CNT_M) >> UART_TXFIFO_CNT_S) < 0x7f);

}

/****************************************************************************
 * Name: esp32_txempty
 *
 * Description:
 *   Return true if the transmit holding and shift registers are empty
 *
 ****************************************************************************/

static bool esp32_txempty(struct uart_dev_s *dev)
{
	struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv;
	return (((esp32_serialin(priv, UART_STATUS_OFFSET) & UART_TXFIFO_CNT_M) >> UART_TXFIFO_CNT_S) == 0);
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: xtensa_early_serial_initialize
 *
 * Description:
 *   Performs the low level UART initialization early in debug so that the
 *   serial console will be available during bootup.  This must be called
 *   before up_serialinit.
 *
 ****************************************************************************/

#ifdef USE_EARLYSERIALINIT
void xtensa_early_serial_initialize(void)
{
	/* NOTE:  All GPIO configuration for the UARTs was performed in
	 * esp32_lowsetup
	 */

	/* Disable all UARTS */

	esp32_disableallints(TTYS0_DEV.priv, NULL);
#ifdef TTYS1_DEV
	esp32_disableallints(TTYS1_DEV.priv, NULL);
#endif
#ifdef TTYS2_DEV
	esp32_disableallints(TTYS2_DEV.priv, NULL);
#endif

	/* Configuration whichever one is the console */

#ifdef HAVE_SERIAL_CONSOLE
	CONSOLE_DEV.isconsole = true;
	esp32_setup(&CONSOLE_DEV);
#endif
}
#endif

/****************************************************************************
 * Name: xtensa_serial_initialize
 *
 * Description:
 *   Register serial console and serial ports.  This assumes
 *   that up_earlyserialinit was called previously.
 *
 ****************************************************************************/

void xtensa_serial_initialize(void)
{
	/* Register the console */
#ifdef HAVE_SERIAL_CONSOLE
	(void)uart_register("/dev/console", &CONSOLE_DEV);
#endif

	/* Register all UARTs */
	(void)uart_register("/dev/ttyS0", &TTYS0_DEV);

#ifdef TTYS1_DEV
	(void)uart_register("/dev/ttyS1", &TTYS1_DEV);
#endif
#ifdef TTYS2_DEV
	(void)uart_register("/dev/ttyS2", &TTYS2_DEV);
#endif

}

/****************************************************************************
 * Name: up_putc
 *
 * Description:
 *   Provide priority, low-level access to support OS debug writes
 *
 ****************************************************************************/

int up_putc(int ch)
{

#ifdef HAVE_SERIAL_CONSOLE
	uint32_t intena;

	esp32_disableallints(CONSOLE_DEV.priv, &intena);

	/* Check for LF */

	if (ch == '\n') {
		/* Add CR */

		while (!esp32_txready(&CONSOLE_DEV)) ;
		esp32_send(&CONSOLE_DEV, '\r');
	}

	while (!esp32_txready(&CONSOLE_DEV)) ;
	esp32_send(&CONSOLE_DEV, ch);
	esp32_restoreuartint(CONSOLE_DEV.priv, intena);

#endif

	return ch;
}
#endif							/* USE_SERIALDRIVER */
